﻿#region Copyright

// The contents of this file are subject to the Mozilla Public License
//  Version 1.1 (the "License"); you may not use this file except in compliance
//  with the License. You may obtain a copy of the License at
//  
//  http://www.mozilla.org/MPL/
//  
//  Software distributed under the License is distributed on an "AS IS"
//  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//  License for the specific language governing rights and limitations under 
//  the License.
//  
//  The Initial Developer of the Original Code is Robert Smyth.
//  Portions created by Robert Smyth are Copyright (C) 2008.
//  
//  All Rights Reserved.

#endregion

using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using NUnitGrid.Framework.Logging;


namespace NUnitGrid.Framework.Pipes.Client
{
    public class PipeClientStream : IPipeStream, IScheduledEventListener
    {
        public delegate void OnConnect(bool succeeded);

        private readonly TimeSpan connectTime = TimeSpan.FromMilliseconds(50);
        private readonly ILogger logger;
        private readonly NamedPipeClientStream pipe;
        private readonly IPipeStream pipeStream;
        private readonly ISchedule schedule;
        private readonly TimeSpan tickPeriod = TimeSpan.FromMilliseconds(25);
        private bool connectionMade;
        private OnConnect onConnectCallBack;
        private Thread readThread;
        private bool waitForConnectionFinished;

        public PipeClientStream(NamedPipeClientStream pipe, IPipeStream pipeStream, ILogger logger, ISchedule schedule)
        {
            this.pipe = pipe;
            this.pipeStream = pipeStream;
            this.logger = logger;
            this.schedule = schedule;
        }

        public void Start()
        {
            schedule.AddEvent(this, tickPeriod);
        }

        public void WriteLine(string message)
        {
            pipeStream.WriteLine(message);
        }

        public void Stop()
        {
            schedule.RemoveEventsFor(this);
            pipeStream.Stop();

            if (readThread.IsAlive)
            {
                readThread.Abort();
                readThread = null;
            }
        }

        public void WaitForConnection(OnConnect onConnect)
        {
            onConnectCallBack = onConnect;
            readThread = new Thread(ReadThreadProc) {Priority = ThreadPriority.Lowest};
            readThread.Start();
        }

        void IScheduledEventListener.OnEventFired()
        {
            if (waitForConnectionFinished)
            {
                waitForConnectionFinished = false;

                if (connectionMade)
                {
                    pipeStream.Start();
                }
                onConnectCallBack(connectionMade);
            }
            schedule.AddEvent(this, tickPeriod);
        }

        private void ReadThreadProc()
        {
            while (!pipe.IsConnected)
            {
                try
                {
                    pipe.Connect((int) connectTime.TotalMilliseconds);
                }
                catch (TimeoutException)
                {
                    Thread.Sleep((int) connectTime.TotalMilliseconds*5);
                }
                catch (IOException exception)
                {
                    logger.Verbose.Write("Attempt to connect IOException '{0}'.", exception.Message);
                }
            }

            connectionMade = pipe.IsConnected;

            if (connectionMade)
            {
                logger.Info.Write("Named pipe connected.");
            }
            else
            {
                logger.Warning.Write("Wait for connection thread exited without connecting pipe.");
            }

            waitForConnectionFinished = true;
        }
    }
}